package com.tpshion.deploy.handler;

import com.alibaba.fastjson.JSON;
import com.tpshion.deploy.common.support.MsgContext;
import com.tpshion.deploy.common.support.MsgData;
import com.tpshion.deploy.handler.msg.support.MsgHandlerFactory;
import com.tpshion.deploy.util.AssertUtil;
import com.tpshion.deploy.common.constant.Constant;
import com.tpshion.deploy.common.support.BusinessException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class MessageHandler extends TextWebSocketHandler {

    private Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();

    @Resource
    private ThreadPoolTaskExecutor msgExecutor;

    @Resource
    private MsgHandlerFactory msgHandlerFactory;

    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
        try {
            session.close(CloseStatus.NOT_ACCEPTABLE.withReason("Binary messages not supported"));
        }catch (IOException e) {
            log.error("handleBinaryMessage error:{}", e.getMessage(), e);
            throw new BusinessException("消息类型不支持");
        }
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String taskId = getTaskId(session);
        log.info("afterConnectionEstablished taskId:{} session:{}", taskId, session);
        WebSocketSession socketSession = sessionMap.get(taskId);
        if(Objects.isNull(socketSession)){
            sessionMap.put(taskId, session);
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        log.info("handleTextMessage message:{}", message.getPayload());
        if(!StringUtils.hasText(message.getPayload())){
            return;
        }
        msgExecutor.execute(() -> {
            try {
                MsgData msgData = JSON.parseObject(message.getPayload(), MsgData.class);
                MsgContext msgContext = new MsgContext(getTaskId(session), msgData, session);
                msgHandlerFactory.getHandler(msgData.getType()).receiveMessage(msgContext);
            }catch (Exception e){
                log.error("handleTextMessage error:{}", e.getMessage(), e);
            }
        });
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("handleTransportError session:{} exception:{}", session, exception, exception.getMessage());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String taskId = getTaskId(session);
        log.info("afterConnectionClosed taskId:{} session:{} status:{}", taskId, session, status);
        sessionMap.remove(taskId);
    }

    public void sendMsg(MsgContext msgContext){
        log.info("sendMsg msgContext:{}", msgContext);
        String taskId = msgContext.getTaskId();
        MsgData msgData = msgContext.getMsgData();
        AssertUtil.isBlank(taskId, "taskId不能为空");
        AssertUtil.isEmpty(msgData, "消息不能为空");

        WebSocketSession socketSession = sessionMap.get(msgContext.getTaskId());
        if(Objects.isNull(socketSession) || !socketSession.isOpen()){
            return;
        }
        msgExecutor.execute(() -> {
            try {
                socketSession.sendMessage(new TextMessage(JSON.toJSONBytes(msgData)));
            } catch (Exception e) {
                log.error("sendMsg error:{}", e.getMessage(), e);
                throw new BusinessException("消息发送失败");
            }
        });
    }

    private String getTaskId(WebSocketSession session){
        Map<String, Object> attributes = session.getAttributes();
        String taskId = (String) attributes.get(Constant.TASK_ID);
        return taskId;
    }
}
